package com.taglab.chic.filter;

import org.springframework.web.filter.GenericFilterBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.File;
import java.util.Map;

import com.taglab.chic.util.UrlUtils;

/**
 *
 */
public class VersionedCachingFilter extends GenericFilterBean {
  private final Logger logger = LoggerFactory.getLogger(getClass());

  private static final String VERSION_QUERY_PARAMETER = "v";

  private String versionQueryParameter = VERSION_QUERY_PARAMETER;

  private long cacheMaxAge = 60 * 60; // Default, 1 hour

  public void setVersionQueryParameter(String versionQueryParameter) {
    this.versionQueryParameter = versionQueryParameter;
  }

  public void setCacheMaxAge(long cacheMaxAge) {
    this.cacheMaxAge = cacheMaxAge;
  }

  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    HttpServletRequest httpRequest = (HttpServletRequest) request;

    if("HEAD".equals(httpRequest.getMethod()) || "GET".equals(httpRequest.getMethod())) {
      // Test if resource has versioning string
      String versioningString = getVersioningFromRequest(httpRequest);
      if(versioningString != null) {
        // Lookup timestamp on local resource
        String localVersioningString = getVersioningFromResource(httpRequest);

        if(versioningString.equals(localVersioningString)) {
          // Set caching headers if underlying resource timestamp matches versioning string
          long expires = System.currentTimeMillis() + cacheMaxAge * 1000;

          logger.debug("adding 'Cache-Control: max-age={} and Expires: {}", cacheMaxAge, expires);

          ((HttpServletResponse) response).addHeader("Cache-Control", "max-age="+ cacheMaxAge);
          ((HttpServletResponse) response).addDateHeader("Expires", expires);
        }
      }
    }

    chain.doFilter(request, response);
  }

  protected String getVersioningFromRequest(HttpServletRequest request) {
    Map<String, String> parameterMap = UrlUtils.parseQueryString(request);
    return parameterMap.get(versionQueryParameter);
  }

  protected String getVersioningFromResource(HttpServletRequest request) {
    ServletContext context = getServletContext();
    String contextFile = UrlUtils.getContextFile(context, request, request.getRequestURI());

    try {
      File localFile = new File(context.getRealPath(contextFile));
      if(localFile.exists()) {
        return UrlUtils.createVersionString(localFile);
      } else {
        logger.warn("Unable to find {} in current context ({}/)", new String[] {contextFile, context.getContextPath()});
      }
    } catch (Exception e) {
      // Container may throw exception if resource does not exist
      logger.warn("Unable to get path of {} in current context ({}/)", new String[] {contextFile, context.getContextPath()}, e);
    }

    return null;
  }
}
